#pragma once

#include "comm/my_defs.h"
#include "comm/my_inet.h"
#include "comm/my_hook_chain.h"
#include "my_conn.h"
#include "my_event_loop.h"

///////////////// tcp listener ////////////////

typedef struct my_tcp_listener_s my_tcp_listener_t;

struct my_tcp_listener_s {
    const char              *listen_addr;
    int                     backlog;
    unsigned                nodelay:1;
    unsigned                quickack:1;
    unsigned                reuseport:1;
    unsigned                reuseaddr:1;
    unsigned                ipv6only:1;

    int                     sockfd;
    my_address_t            address;
    my_event_handler_t      accept_handler;

    my_tcp_listener_t       *next;
};

extern my_tcp_listener_t g_tcp_listener_list;


static inline void
my_tcp_listener_init(my_tcp_listener_t *listener)
{
    listener->sockfd = INVALID_FD;
}


// listener must be register before my_tcp_accepter_start.
static inline void
my_tcp_listener_register(my_tcp_listener_t *listener)
{
    listener->next = g_tcp_listener_list.next;
    g_tcp_listener_list.next = listener;
}


////////////////// tcp accepter /////////////////

typedef struct {
    int                         nthreads;
    my_event_loop_data_t        *datas;
    my_thread_base_t            *threads;
    my_hook_chain_t             *init_hook_chain;
    my_hook_chain_t             *quit_hook_chain;
} my_tcp_accepter_t;

extern my_tcp_accepter_t g_tcp_accepter;


// hook_add must be called before my_tcp_accepter_start.
// hook_handler(loop_data)
void my_tcp_accepter_add_init_hook(my_hook_handler_pt hook_handler);

// hook_add must be called before my_tcp_accepter_start.
// hook_handler(loop_data)
void my_tcp_accepter_add_quit_hook(my_hook_handler_pt hook_handler);

// thread_data_slot_alloc must called before my_tcp_accepter_start.
int my_tcp_accepter_thread_data_slot_alloc();

void my_tcp_accepter_start(int nthreads);

void my_tcp_accepter_stop();

extern char *my_tcp_accept_thread_tag;

static inline my_bool_t
current_thread_is_tcp_accepter_thread()
{
    my_thread_base_t *thread_base = current_thread_base();
    if (thread_base == NULL) {
        return MY_FALSE;
    }

    if (thread_base->tag != my_tcp_accept_thread_tag) {
        return MY_FALSE;
    }

    return MY_TRUE;
}


//////////////// tcp connect ////////////////////

typedef struct {
    unsigned                nodelay:1;
    unsigned                quickack:1;
} my_tcp_connect_opt_t;


typedef void (*my_tcp_connect_handler_pt)(void *data,
    my_conn_t *conn, my_result_t res);

void my_tcp_connect_setup(
    my_address_t *address,
    int64_t timeout_ms,
    my_tcp_connect_opt_t *opts,
    my_tcp_connect_handler_pt connect_handler,
    void *data);
